﻿namespace PugLib.UnitTest
{
	using System;
	using System.Threading;
	using System.Threading.Tasks;

	using Microsoft.VisualStudio.TestTools.UnitTesting;

	using PugLib;

	/// <summary>
	/// A test class for QueuedActions
	///</summary>
	[TestClass]
	public class QueuedActionsTest
	{
		/// <summary>
		/// A test for ActionQueue.
		/// </summary>
		[CssIteration("vstfs:///Classification/Node/69672ec4-35ad-437d-8d2c-73257d1f5ef6")]
		[CssProjectStructure("vstfs:///Classification/Node/1d37a06e-0d30-40a0-9200-1369acddd9a2")]
		[Owner("brent.miller@shavlik.com")]
		[TestMethod]
		public void QueuedActionsTest_Enqueue_Succeeds()
		{
			using (QueuedActions<Int32> queue = new QueuedActions<Int32>(101))
			{
				for (Int32 i = 0; i < 100; ++i)
				{
					queue.Enqueue(ComputeNumber, i);
				}
				Assert.AreEqual(0, queue.ProcessedCount);
			}
		}

		/// <summary>
		/// A test for ActionQueue.
		/// </summary>
		[CssIteration("vstfs:///Classification/Node/69672ec4-35ad-437d-8d2c-73257d1f5ef6")]
		[CssProjectStructure("vstfs:///Classification/Node/1d37a06e-0d30-40a0-9200-1369acddd9a2")]
		[Owner("brent.miller@shavlik.com")]
		[TestMethod]
		public void QueuedActionsTest_Process_Succeeds()
		{
			const Int32 actionCount = 100;

			using (CountdownEvent completion = new CountdownEvent(actionCount))
			using (QueuedActions<Int32> queue = new QueuedActions<Int32>())
			{
				for (Int32 i = 0; i < actionCount; ++i)
				{
					queue.Enqueue(ComputeNumber, i);
				}

				queue.Process(completion);

				Assert.AreEqual(actionCount, queue.ProcessedCount);
			}
		}

		/// <summary>
		/// A test for ActionQueue.
		/// </summary>
		[CssIteration("vstfs:///Classification/Node/69672ec4-35ad-437d-8d2c-73257d1f5ef6")]
		[CssProjectStructure("vstfs:///Classification/Node/1d37a06e-0d30-40a0-9200-1369acddd9a2")]
		[Owner("brent.miller@shavlik.com")]
		[TestMethod]
		public void QueuedActionsTest_ProcessWithAdditionlQueue_Succeeds()
		{
			const Int32 actionCount = 100;

			using (CountdownEvent completion = new CountdownEvent(actionCount * 2))
			using (QueuedActions<Int32> queue = new QueuedActions<Int32>())
			{
				Task firstEnqueing = Task.Factory.StartNew(() =>
				                                           {
				                                           	for (Int32 i = 0; i < actionCount; ++i)
				                                           	{
				                                           		queue.Enqueue(ComputeNumber, i);
				                                           	}
				                                           });

				Task secondEnqueing = Task.Factory.StartNew(() =>
				                                            {
				                                            	for (Int32 i = 0; i < actionCount; ++i)
				                                            	{
				                                            		queue.Enqueue(ComputeNumber, i);
				                                            	}
				                                            });
				queue.Process(completion);

				Task.WaitAll(firstEnqueing, secondEnqueing);

				Assert.AreEqual(actionCount * 2, queue.ProcessedCount);
			}
		}

		/// <summary>
		/// A test for ActionQueue.
		/// </summary>
		[CssIteration("vstfs:///Classification/Node/69672ec4-35ad-437d-8d2c-73257d1f5ef6")]
		[CssProjectStructure("vstfs:///Classification/Node/1d37a06e-0d30-40a0-9200-1369acddd9a2")]
		[Owner("brent.miller@shavlik.com")]
		[TestMethod]
		public void QueuedActionsTest_ParallelProcess_Succeeds()
		{
			using (ManualResetEventSlim completion = new ManualResetEventSlim(false))
			using (QueuedActions<Int32> queue = new QueuedActions<Int32>())
			{
				const Int32 sleepCount = 10;
				const Int32 actionCount = 10;
				Parallel.For(0, actionCount, value => queue.Enqueue(ComputeNumber, sleepCount));

				queue.Process();

				completion.Wait(TimeSpan.FromMilliseconds((sleepCount * actionCount) + 5000)); // 5 second buffer
				Assert.AreEqual(actionCount, queue.ProcessedCount);
			}
		}

		/// <summary>
		/// A test for ActionQueue.
		/// </summary>
		[CssIteration("vstfs:///Classification/Node/69672ec4-35ad-437d-8d2c-73257d1f5ef6")]
		[CssProjectStructure("vstfs:///Classification/Node/1d37a06e-0d30-40a0-9200-1369acddd9a2")]
		[Owner("brent.miller@shavlik.com")]
		[TestMethod]
		public void QueuedActionsTest_ProcessCancel_Succeeds()
		{
			const Int32 actionCount = 100;

			using (QueuedActions<Int32> queue = new QueuedActions<Int32>())
			{
				for (Int32 i = 0; i < actionCount; ++i)
				{
					queue.Enqueue(ComputeNumber, i);
				}

				Thread.Sleep(1000);
				queue.Process();
				// now dispose and test cancel doesn't throw an unhandled exception
			}
		}

		/// <summary>
		/// A test for ActionQueue.
		/// </summary>
		[CssIteration("vstfs:///Classification/Node/69672ec4-35ad-437d-8d2c-73257d1f5ef6")]
		[CssProjectStructure("vstfs:///Classification/Node/1d37a06e-0d30-40a0-9200-1369acddd9a2")]
		[Owner("brent.miller@shavlik.com")]
		[TestMethod]
		public void QueuedActionsTest_ProcessCancelWithCountdown_Succeeds()
		{
			const Int32 actionCount = 100;

			using (CountdownEvent completion = new CountdownEvent(actionCount))
			using (QueuedActions<Int32> queue = new QueuedActions<Int32>())
			{
				for (Int32 i = 0; i < actionCount; ++i)
				{
					queue.Enqueue(ComputeNumber, i);
				}

				Task.Factory.StartNew(() =>
				                      {
				                      	Thread.Sleep(1000);
				                      	queue.Dispose();
				                      });

				queue.Process(completion);
			}
		}

		private static void ComputeNumber(Int32 number)
		{
			Thread.Sleep(number);
		}
	}
}